package persistence;

import java.util.Date ;
import java.util.HashSet ;
import java.util.Set ;
import java.util.concurrent.ConcurrentHashMap ;
import java.util.concurrent.ConcurrentMap ;

import persistence.xstream.XStreamPersistenceController ;





/**
 * The Class Index which holds all the data structures for the data base and
 * perform actions on them.
 */
public class OriginIndex{

	/** The map of words and their id. */
	private final ConcurrentMap<String, Long> words;// word name,word id

	/** The map of messages and their id which are the posts id that has the message. */
	private final ConcurrentMap<String, Long> messages;// message context,message id

	/** The map of relations between a word and all the messages that the word is in them. */
	private final ConcurrentMap<Long, Set<Long>> wordInMessages;//word id,message id

	/** The map of authors user names and their ids. */
	private final ConcurrentMap<String, Long> authors;//author username,author id

	/** The map of dates and  of all the messages that were sent on that date. */
	private final ConcurrentMap<Date,  Set<Long>> dateOfMessage;//date,message id

	/** The map of relations between author user name and of  the messages he wrote. */
	private final ConcurrentMap<Long, Set<Long>> authorOfMessages;//author id,mesage id

	private final ConcurrentMap<Date,  Set<Long>> dateOfLogins ;
	private final ConcurrentMap<Long, Date> messageDates ;

	private String indexUri;

	/**
	 * Instantiates a new index and creates the data structures.
	 * @param indexUri the uri to save this index to persistence
	 */
	public OriginIndex(final String indexUri)
	{
		this.indexUri = indexUri;
		this.words = new ConcurrentHashMap<String, Long>();
		this.messages = new ConcurrentHashMap<String, Long>();
		this.wordInMessages = new ConcurrentHashMap<Long, Set<Long>>();
		this.authors = new ConcurrentHashMap<String, Long>();
		this.dateOfMessage = new ConcurrentHashMap<Date, Set<Long>>();
		this.authorOfMessages = new ConcurrentHashMap<Long, Set<Long>>();
		this.dateOfLogins = new ConcurrentHashMap<Date, Set<Long>>();
		this.messageDates = new ConcurrentHashMap<Long, Date>() ;
		//new set would be done when i put objects in the maps
	}

	/**
	 * Contains word - checks if the map of words contains a certain word.
	 *
	 * @param wordName the word name
	 *
	 * @return true, if successful
	 */
	public boolean containsWord(final String wordName)
	{
		if (this.words.containsKey(wordName))
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	/**
	 * Gets the author id by user name.
	 *
	 * @param userName the user name
	 *
	 * @return the author id
	 *

	 */
	public Long getAuthorByUserName (final String userName)
	{
		final Long ans = this.authors.get(userName);
		return ans;
	}



	/**
	 * @param from start date.
	 * @param to end date.
	 * @return list of login id's between these dates.
	 */
	public Set<Long> getLoginsByDate(final Date from, final Date to)
	{
		Set<Long> result = new HashSet<Long>() ;
		for (Date date: this.dateOfLogins.keySet())
		{
			if (date.after(from) && date.before(to))
			{
				result.addAll(this.dateOfLogins.get(date)) ;
			}
		}
		return result ;
	}

	/**
	 * Gets the message by its context.
	 *
	 * @param context the context
	 *
	 * @return the long
	 *

	 */
	public Long getMessageByContext(final String context)
	{
		final Long ans = this.messages.get(context);
		return ans;
	}

	/**
	 * Gets the messages that were sent by a certain author- by author id.
	 *
	 * @param authorId the author id
	 *
	 * @return Set<Long> - the messages that were sent by a certain author
	 *

	 */
	public Set<Long> getMessagesByAuthorId(final Long authorId)
	{
		final Set<Long> ans = this.authorOfMessages.get(authorId);
		if (ans.equals(null))
		{
			return new HashSet<Long>();
		}
		else
		{
			return ans;
		}
	}

	/**
	 * @param authorId if of the message author.
	 * @param from start date.
	 * @param to end date.
	 * @return list of message ids which were posed by this author between these dates.
	 */
	public Set<Long> getMessagesByAuthorId(final Long authorId, final Date from, final Date to)
	{
		Set<Long> source = this.getMessagesByAuthorId(authorId) ;
		Set<Long> result = new HashSet<Long>() ;
		for (Long id: source)
		{
			if (this.messageDates.get(id).after(from) && this.messageDates.get(id).before(to))
			{
				result.add(id) ;
			}
		}
		return result ;
	}

	/**
	 * Gets the messages that were sent on a certain date by date.
	 *
	 * @param date -the date
	 *
	 * @return Set<Long> - the messages that were sent on a certain date
	 *

	 */
	public Set<Long> getMessagesByDate (final Date date)
	{
		final Set<Long> ans = this.dateOfMessage.get(date);
		if (ans.equals(null))
		{
			return new HashSet<Long>();
		}
		else
		{
			return ans;
		}
	}

	/**
	 * @param from start date.
	 * @param to end date.
	 * @return list of post id's which were posted between these dates.
	 */
	public Set<Long> getMessagesByDate(final Date from, final Date to)
	{
		Set<Long> result = new HashSet<Long>() ;
		for (Date date: this.dateOfMessage.keySet())
		{
			if (date.after(from) && date.before(to))
			{
				result.addAll(this.dateOfMessage.get(date)) ;
			}
		}
		return result ;
	}

	/**
	 * Gets all the messages that the word appears in them by  word id.
	 *
	 * @param wordId the word id
	 *
	 * @return  Set<Long> the set of messages that the word appears in them
	 *

	 */
	public Set<Long> getWordInMessagesByWordId(final Long wordId)
	{
		final Set<Long> ans = this.wordInMessages.get(wordId);
		if (ans.equals(null))
		{
			return new HashSet<Long>();
		}
		else
		{
			return ans;
		}


	}

	/**
	 * Gets the words by name.
	 *
	 * @param name the name
	 *
	 * @return the words by the word name
	 *
	 */
	public Long getWordsByName (final String name)
	{
		final Long ans = this.words.get(name);
		return ans;
	}

	/**
	 * Instantiates a new index from persistence.
	 * @param indexURI the uri where index was saved
	 * @return an instance of index loaded from persistence
	 * @throws CorruptDataException if data read from the persistent index is not as expected.
	 */
	public OriginIndex loadIndex(final String indexURI) throws CorruptDataException
	{
		Object temp ;
		temp = XStreamPersistenceController.loadFromUri(indexURI) ;
		if (temp instanceof OriginIndex)
		{
			((OriginIndex)temp).indexUri = indexURI;
			return (OriginIndex)temp ;
		}
		else
		{
			throw new CorruptDataException() ;
		}
	}

	/**
	 * Put author in map by user name.
	 *
	 * @param authorUserName the author user name
	 * @param authorId the author id
	 */
	public void putAuthorByUserName(final String authorUserName,final Long authorId)
	{
		this.authors.put(authorUserName, authorId);
	}

	/**
	 * insert a login to the index.
	 * @param id id of the login.
	 * @param loginTime date of the login.
	 */
	public void putLogin(final long id, final Date loginTime)
	{
		if (!this.dateOfLogins.containsKey(loginTime))
		{
			this.dateOfLogins.put(loginTime, new HashSet<Long>()) ;
		}
		this.dateOfLogins.get(loginTime).add(id) ;
	}

	/**
	 * Put message by context of message.
	 *
	 * @param context the context of message which is the message of a post
	 * @param messageId the message id
	 */
	public void putMessageByContext(final String context,final Long messageId)
	{
		this.messages.put(context, messageId);
	}

	/**
	 * Put message in set of message for author.
	 * checks if already exists a set of messages for the author id
	 * @param authorId the author id
	 * @param messageId the message id
	 */
	public void putMessageInSetOfMessageForAuthor(final Long authorId,final Long messageId)
	{
		if (!this.authorOfMessages.containsKey(authorId))
		{
			this.authorOfMessages.put(authorId, new HashSet<Long>());
		}
		this.authorOfMessages.get(authorId).add(messageId);
	}

	/**
	 * Put message in set of message for  a certain date.
	 * checks if already exists a set of messages for the date
	 * @param date the date
	 * @param messageId the message id
	 */
	public void putMessageInSetOfMessageForDate(final Date date,final Long messageId)
	{
		if (!this.dateOfMessage.containsKey(date))
		{
			this.dateOfMessage.put(date, new HashSet<Long>());
		}
		this.dateOfMessage.get(date).add(messageId);
		this.messageDates.put(messageId, date) ;
	}

	/**
	 * Put message in set of messages for a certain word.
	 * checks if already exists a set of messages for the word
	 * @param wordId the word id
	 * @param messageId the message id
	 */
	public void putMessageInSetOfMessagesForWord(final Long wordId,final Long messageId)
	{
		if (!this.wordInMessages.containsKey(wordId))
		{
			this.wordInMessages.put(wordId, new HashSet<Long>());
		}
		this.wordInMessages.get(wordId).add(messageId);
	}

	/**
	 * Put word by name in the map.
	 *
	 * @param wordName the word name
	 * @param wordId the word id
	 */
	public void putWordByName(final String wordName,final Long wordId)
	{
		this.words.put(wordName, wordId);
	}

	/**
	 * save inedx to persistence
	 */
	public void saveIndex()
	{
		XStreamPersistenceController.saveToUri(this.indexUri, this) ;
	}


}
